/* 
 * krawsock - kernel raw socket library 
 * 
 *      Author: SaeedM <saeed.tx@gmail.com> , Aug-2011
 *
 * 
 * 	This program is free software; you can redistribute it and/or
 *      modify it under the terms of the GNU General Public License
 *      as published by the Free Software Foundation; either version
 *      2 of the License, or (at your option) any later version.
 */

#include "kraweth_data.h"
#include "krawsock.h"

struct socket* krsocket_open(char* ifname, int recvbuffsize,int sndbuffsize){
	int err;
	struct socket* sk=NULL;
	err = sock_create(PF_PACKET,SOCK_RAW,  htons(ETH_P_ALL)  , &sk); 
        if (err<0) 
        { 
                KLOG("failed to create raw eth socket\n"); 
                return NULL; 
        }
	if (ifname && (err = kernel_setsockopt(sk, SOL_SOCKET, SO_BINDTODEVICE,ifname,strlen(ifname) )!= 0)){
  	     	KLOG("failed to bind kernel socket to %s\n",ifname);
                goto out_err;
	} 
               
	if (sndbuffsize && (err = kernel_setsockopt(sk, SOL_SOCKET, SO_SNDBUF,(char*)&sndbuffsize, sizeof(int))) != 0){
                KLOG("failed to set socket snd buffer size to 0\n");
                goto out_err;
        } 

	if (recvbuffsize && (err = kernel_setsockopt(sk, SOL_SOCKET, SO_RCVBUF,(char*)&recvbuffsize, sizeof(int))) != 0){
                KLOG("failed to set socket recv buffer size to \n");
                goto out_err;
        }    
	return sk;

out_err:
	krsocket_close(sk);
	return NULL;	
}

void krsocket_close(struct socket* sk){
	if(sk)     
		sock_release(sk); 	
}

ssize_t krecv(struct socket *sk, void *buffer, size_t length, int flags){
        //struct socket *sk;
        struct msghdr msg;
        struct iovec iov;
        int ret;
#ifndef KSOCKET_ADDR_SAFE
        mm_segment_t old_fs;
#endif

        //sk = (struct socket *)socket;

        iov.iov_base = (void *)buffer;
        iov.iov_len = (__kernel_size_t)length;

        msg.msg_name = NULL;
        msg.msg_namelen = 0;
        msg.msg_iov = &iov;
        msg.msg_iovlen = 1;
        msg.msg_control = NULL;
        msg.msg_controllen = 0;

        /*
         * msg.msg_iov->iov_base is declared as follows:
         * void __user *iov_base;
         * which means there is an user space pointer in 'msg'
         * use set_fs(KERNEL_DS) to make the pointer safe to kernel space
         */
#ifndef KSOCKET_ADDR_SAFE
        old_fs = get_fs();
        set_fs(KERNEL_DS);
#endif
        ret = sock_recvmsg(sk, &msg, length, flags);
#ifndef KSOCKET_ADDR_SAFE
        set_fs(old_fs);
#endif
        if (ret < 0)
                goto out_krecv;
        //ret = msg.msg_iov.iov_len;//?

out_krecv:
        return ret;

}

ssize_t ksend(struct socket* sk, const void *buffer, size_t length, int flags,int ifindex){
	struct msghdr msg;
	struct iovec iov;
	struct sockaddr_ll addr_ll;
	int len = length;
#ifndef KSOCKET_ADDR_SAFE
	mm_segment_t old_fs;
#endif
	memset(&addr_ll,0,sizeof(struct sockaddr_ll));
	iov.iov_base = (void *)buffer;
	iov.iov_len = (__kernel_size_t)length;
//      addrll.proto   = saddr->sll_protocol;
//      addr    = saddr->sll_addr;
	addr_ll.sll_ifindex = ifindex;
	msg.msg_name = &addr_ll;
	msg.msg_namelen = sizeof(struct sockaddr_ll);
	msg.msg_iov = &iov;
	msg.msg_iovlen = 1;
	msg.msg_control = NULL;
	msg.msg_controllen = 0;

	msg.msg_flags = flags;

#ifndef KSOCKET_ADDR_SAFE
	old_fs = get_fs();
	set_fs(KERNEL_DS);
#endif
	len = sock_sendmsg(sk, &msg, length);
#ifndef KSOCKET_ADDR_SAFE
	set_fs(old_fs);
#endif
	
	return len;
}


/*

unsigned int inet_addr(char* ip);

char *inet_ntoa(struct in_addr *in); 

*/
